package com.ruixin.mvc.config;

import com.alibaba.fastjson.JSONObject;
import com.ruixin.base.BaseTryCatch;
import com.ruixin.base.exception.BaseException;
import com.ruixin.base.factory.Factory;
import com.ruixin.base.util.BeanUtil;
import com.ruixin.base.util.StringUtil;
import com.ruixin.base.util.TypeConvertor;
import com.ruixin.config.BeanConstant;
import com.ruixin.mvc.util.PathUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.Map;

/**
 * 适配器
 */
public class RequestHandlerAdaptor {

    protected ApplicationContextWrapper context;

    public RequestHandlerAdaptor(ApplicationContextWrapper context){
        this.context = context;
    }

    /**
     * 获取请求连接
     * @param request
     * @return
     */
    private String getUrl(HttpServletRequest request){
        String contextPath = this.context.getContextPath();
        String requestURI = request.getRequestURI();
        contextPath = PathUtil.formatPath(contextPath);
        String url = requestURI.replace(contextPath, "");
        return PathUtil.formatPath(url);
    }

    /**
     * 处理请求
     * @param request
     * @param response
     * @throws BaseException
     */
    public void hander(HttpServletRequest request, HttpServletResponse response) throws BaseException {
        String url = getUrl(request);
        invokeMapping(request,response,url);
    }

    /**
     * 调用mapping
     * @param request
     * @param response
     * @param url
     * @throws BaseException
     */
    public void invokeMapping(HttpServletRequest request, HttpServletResponse response,String url) throws BaseException {
        Factory<Class, WebMapping> webMappingFactory = this.context.getWebMappingFactory();
        for (WebMapping webMapping : webMappingFactory.values()) {
            String path = webMapping.getPath();
            if(StringUtil.isBlackOrNull(path)||url.startsWith(path)){
                Factory<Method, MethodMapping> methodMappingFactory = webMapping.getMethodMappingFactory();
                for (MethodMapping methodMapping : methodMappingFactory.values()) {
                    if(!isAllowType(request,methodMapping)){ //判断请求类型是否支持
                        continue;
                    }
                    String[] paths = methodMapping.getPaths();
                    for (String mappingPath : paths) {
                        if(PathUtil.match(url,path,mappingPath)){
                            Method method = methodMapping.getMethod();
                            Class<?> beanClass = method.getDeclaringClass();
                            Object bean = this.context.getBean(beanClass);
                            Object[] objects = setParameter(request, response, methodMapping, PathUtil.getPath(path, mappingPath));
                            Object result = BeanUtil.invokeMethod(bean,method,objects);
                            if(methodMapping.isJsonResult()){
                                setJsonResult(response,result);
                            }else if (result instanceof String){
                                String urlResult = TypeConvertor.toString(result);
                                if(urlResult.startsWith(BeanConstant.STR_REDIRECT)){ //Redirect
                                    String redirectUrl = urlResult.replace(BeanConstant.STR_REDIRECT,"");
                                    BaseTryCatch.process(()->{
                                        response.sendRedirect(redirectUrl);
                                    });
                                }else { //view
                                    setView(request,response,urlResult);
                                }
                            }
                            return;
                        }
                    }
                }
            }
        }
        throw new BaseException("找不到对应的Mapping");
    }

    /**
     * 设置请求参数
     * @param request
     * @param response
     * @param methodMapping
     * @return
     */
    public Object[] setParameter(HttpServletRequest request,HttpServletResponse response,MethodMapping methodMapping,String path) throws BaseException {
        Method method = methodMapping.getMethod();
        Factory<Parameter, ParameterMapping> argMappingFactory = methodMapping.getArgMappingFactory();
        List<Parameter> requestBodys = methodMapping.getRequestBodys();
        Object[] args = new Object[method.getParameterCount()];
        Parameter[] parameters = method.getParameters();
        String url = getUrl(request);
        Map<String, String> pathVariables = PathUtil.getPathVariable(url, path);
        for(int i=0;i<method.getParameterCount();i++){
            Parameter parameter = parameters[i];
            Class<?> type = parameter.getType();
            if(type == HttpServletRequest.class){
                args[i] = request;
            }else if(type == HttpServletResponse.class){
                args[i] = response;
            }else{
                if(argMappingFactory.containsKey(parameter)){
                    ParameterMapping parameterMapping = argMappingFactory.get(parameter);
                    String name = parameterMapping.getName();
                    if(parameterMapping.isArg()){
                        String value = request.getParameter(name);
                        if(parameterMapping.isRequired()&&StringUtil.isBlackOrNull(value)){
                            throw new BaseException(method.getName()+"方法@Args(value="+name+",required=true) 但是没找到相关参数");
                        }
                        args[i] = TypeConvertor.parseData(value,type);
                        continue;
                    }else{
                        String value = pathVariables.get(name);
                        if(parameterMapping.isRequired()&&StringUtil.isBlackOrNull(value)){
                            throw new BaseException(method.getName()+"方法@PatchVariable(value="+name+",required=true) 但是没找到相关参数");
                        }
                        args[i] = TypeConvertor.parseData(value,type);
                        continue;
                    }
                }else if(requestBodys.contains(parameter)){
                    args[i] = setRequestBody(request,type);
                    continue;
                }
                throw new BaseException(method.getName()+"方法参数缺少@Args注解");
            }
        }
        return args;
    }

    /**
     * 处理RequestBody
     * @param request
     * @param type
     * @return
     * @throws BaseException
     */
    public Object setRequestBody(HttpServletRequest request,Class<?> type) throws BaseException {
        Object instance = BeanUtil.newInstance(type);
        Field[] declaredFields = type.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            String name = declaredField.getName();
            Class<?> fieldType = declaredField.getType();
            String value = request.getParameter(name);
            BeanUtil.invokeField(instance,declaredField,TypeConvertor.parseData(value,fieldType));
        }
        return instance;
    }
    
    /**
     * 返回JSON格式数据
     * @param response
     * @param result
     */
    public void setJsonResult(HttpServletResponse response,Object result) throws BaseException {
        BaseTryCatch.process(()->{
            response.setContentType("application/json;charset=UTF-8");
            String jsonString = JSONObject.toJSONString(result);
            PrintWriter writer = response.getWriter();
            writer.write(jsonString);
            writer.flush();
        });
    }

    /**
     * 是否是支持的请求类型
     * @param request
     * @param methodMapping
     * @return
     * @throws BaseException
     */
    public boolean isAllowType(HttpServletRequest request,MethodMapping methodMapping) throws BaseException {
        String method = request.getMethod();
        RequestType type = RequestType.parse(method);
        RequestType[] requestTypes = methodMapping.getRequestTypes();
        for (RequestType requestType : requestTypes) {
            if(type.equals(requestType)){
                return true;
            }
        }
        return false;
    }

    /**
     * view处理
     * @param request
     * @param response
     * @param url
     * @throws BaseException
     */
    public void setView(HttpServletRequest request,HttpServletResponse response,String url) throws BaseException {
        EnvironmentWrapper environment = this.context.getEnvironment();
        String viewPrefix = environment.getViewPrefix();
        String viewSubfix = environment.getViewSubfix();
        String path = PathUtil.getPath(viewPrefix, url);
        BaseTryCatch.process(()->{
            request.getRequestDispatcher(path+viewSubfix).forward(request,response);
        });
    }
}
